package biolabs.gui;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.Closeable;

import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import biolabs.core.Application;
import biolabs.core.BioObject;
import biolabs.core.Experiment;
import biolabs.sink.Sink;
import biolabs.visual.VisualizationConfiguration;
import biolabs.visual.VisualizationMethod;
import biolabs.visual.j2d.Java2D$;
import biolabs.visual.j3d.Java3D$;

/**
 * Part of the BioLabs' GUI that displays some details
 * about the currently selected experiment.
 *
 * @author Tomas Mikula
 */
class ExperimentDetail implements UIComponent,
		ExperimentSelectionListener {

	private static final VisualizationMethod<?>[] METHODS = new VisualizationMethod[] {
		Java2D$.MODULE$,
		Java3D$.MODULE$,
	};

	private static final ImageIcon EYE = new ImageIcon(ExperimentDetail.class.getResource("eye.png"));

	private final Application app;
	private final JComponent content = Box.createVerticalBox();
	private final JLabel name = new JLabel();
	private final JComboBox visMethodSelector = new JComboBox();
	private final JButton visButton = new JButton(EYE);
	private final JComponent[] controls = new JComponent[] {
		visMethodSelector,
		visButton,
	};

	private Experiment currentExperiment = null;

	public ExperimentDetail(Application app) {
		this.app = app;

		visButton.setToolTipText("Visualize");
		visButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent evt) {
				visualize();
			}
		});

		for(VisualizationMethod<?> m: METHODS)
			visMethodSelector.addItem(m);

		JPanel vis = new JPanel();
		vis.add(visMethodSelector);
		vis.add(visButton);

		name.setAlignmentX(0.5f);
		vis.setAlignmentX(0.5f);

		content.add(name);
		content.add(vis);

		clear();
	}

	@Override
	public Component getContent() {
		return content;
	}


	@Override
	public void experimentSelected(Experiment exp) {
		if(exp == currentExperiment)
			return;
		if(exp == null)
			clear();
		else
			load(exp);
	}

	private void clear() {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				name.setText(null);
				disableControls();
			}
		});
		currentExperiment = null;
	}

	private void load(final Experiment exp) {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				name.setText(exp.getName());
				enableControls();
			}
		});
		currentExperiment = exp;
	}

	private void enableControls() {
		for(JComponent c: controls)
			c.setEnabled(true);
	}

	private void disableControls() {
		for(JComponent c: controls)
			c.setEnabled(false);
	}

	private void visualize() {
		// Obtain the selected visualization method and
		// check whether it supports on-screen visualization.
		VisualizationMethod<?> method = (VisualizationMethod<?>) visMethodSelector.getSelectedItem();
		if(!method.supportsOnScreen()) {
			app.getLogger().warning(method.name() + " does not support on-screen visualization.");
			return;
		}

		// Obtain the experiment's root object.
		final BioObject root = currentExperiment.getRoot();
		if(root == null) {
			app.getLogger().warning("Experiment " + currentExperiment.getName() + " contains no objects to visualize.");
			return;
		}

		// Obtain application's default visualization configuration for the
		// desired visual representation and check whether it specifies
		// a suitable visualizer for the experiment's root object.
		final VisualizationConfiguration<?> cfg = app.getDefaultVisualizationConfiguration(method);
		if(!root.canVisualize(cfg)) {
			app.getLogger().warning("There is no default visualization configured for " + root.getClass().getName());
			return;
		}

		// create the visualization window
		final WindowSink window = method.createWindow().get();

		// visualize on each experiment stop (including now)
		Closeable handle = cfg.apply(root, currentExperiment.asReadLockedClock(), currentExperiment.getStep(), new Sink[]{ window });
		window.setVisualizationHandle(handle);

		// make the window visible
		window.show();
	}

}
